import { shuffle, union } from "lodash"

import { getLanguageCodeName } from "@/lib/utils/intl"
import { capitalize } from "@/lib/utils/string"

import { newToCrypto } from "@/data/wallets/new-to-crypto"
import walletsData from "@/data/wallets/wallet-data"

import {
  DEVELOPER_FEATURES,
  FINANCE_FEATURES,
  LONG_TERM_FEATURES,
  NEW_TO_CRYPTO_FEATURES,
  NFTS_FEATURES,
} from "../constants"
import type {
  ChainName,
  FilterOption,
  Lang,
  WalletData,
  WalletRow,
} from "../types"

export const getSupportedLocaleWallets = (locale: string) =>
  shuffle(
    walletsData.filter((wallet) =>
      wallet.languages_supported.includes(locale as Lang)
    )
  )

export const getNonSupportedLocaleWallets = (locale: string) =>
  shuffle(
    walletsData.filter(
      (wallet) => !wallet.languages_supported.includes(locale as Lang)
    )
  )

export const getNewToCryptoWallets = () => {
  return walletsData.filter((wallet) => newToCrypto.includes(wallet.name))
}

// Get a list of a wallet supported Personas (new to crypto, nfts, long term, finance, developer)
export const getWalletPersonas = (wallet: WalletData) => {
  const walletPersonas: string[] = []

  const isNewToCryptoPersona = NEW_TO_CRYPTO_FEATURES.every(
    (feature) => wallet[feature]
  )
  const isNFTPersona = NFTS_FEATURES.every((feature) => wallet[feature])
  const isLongTermPersona = LONG_TERM_FEATURES.every(
    (feature) => wallet[feature]
  )
  const isFinancePersona = FINANCE_FEATURES.every((feature) => wallet[feature])
  const isDeveloperPersona = DEVELOPER_FEATURES.every(
    (feature) => wallet[feature]
  )

  if (isNewToCryptoPersona) {
    walletPersonas.push("page-find-wallet-new-to-crypto-title")
  }

  if (isNFTPersona) {
    walletPersonas.push("page-find-wallet-nfts-title")
  }

  if (isLongTermPersona) {
    walletPersonas.push("page-find-wallet-hodler-title")
  }

  if (isFinancePersona) {
    walletPersonas.push("page-find-wallet-finance-title")
  }

  if (isDeveloperPersona) {
    walletPersonas.push("page-find-wallet-developer-title")
  }

  return walletPersonas
}

// Get a list of wallet supported languages with native title
export const getSupportedLanguages = (
  supportedLanguageCodes: string[],
  locale: string
) => {
  const supportedLanguages = [] as string[]

  // current locale should appear first on the list, this manipulates the array to move it to the top if needed
  const supportsCurrentLocale = (current) => current === locale
  const localeIndex = supportedLanguageCodes.findIndex(supportsCurrentLocale)

  if (localeIndex >= 0) {
    supportedLanguageCodes.splice(localeIndex, 1)
    supportedLanguageCodes.unshift(locale)
  }

  supportedLanguageCodes.forEach((supportedLanguage) => {
    // Get supported language name
    const supportedLanguageName = getLanguageCodeName(supportedLanguage, locale)
    // Capitalize supported language name
    supportedLanguages.push(capitalize(supportedLanguageName!))
  })

  return supportedLanguages
}

// Format languages list to be displayed on UI label
export const formatStringList = (strings: string[], sliceSize?: number) => {
  return sliceSize ? strings.slice(0, sliceSize).join(", ") : strings.join(", ")
}

// Get total count of wallets that support a language
const getLanguageTotalCount = (languageCode: string) => {
  return walletsData.reduce(
    (total, currentWallet) =>
      currentWallet.languages_supported.includes(languageCode as Lang)
        ? (total = total + 1)
        : total,
    0
  )
}

// Get a list of all wallets languages, without duplicates
export const getAllWalletsLanguages = (locale: string) => {
  const compareFn = (
    a: { langCode: string; langName: string },
    b: { langCode: string; langName: string }
  ) => {
    if (a.langName > b.langName) {
      return 1
    }
    if (a.langName < b.langName) {
      return -1
    }
    return 0
  }

  return (
    walletsData
      .reduce(
        (allLanguagesList, current) =>
          // `union` lodash method merges all arrays removing duplicates
          union(allLanguagesList, current.languages_supported),
        [] as string[]
      )
      .map((languageCode) => {
        // Get supported language name
        const supportedLanguageName = getLanguageCodeName(languageCode, locale)
        // Get a list of {langCode, langName}
        return {
          langCode: languageCode,
          langName: `${capitalize(
            supportedLanguageName!
          )} (${getLanguageTotalCount(languageCode)})`,
        }
      })
      // Sort list alphabetically by langName
      .sort(compareFn)
  )
}

export const getLanguageCountWalletsData = (locale: string) => {
  const languageCountWalletsData = getAllWalletsLanguages(locale).map(
    (language) => ({
      langCode: language.langCode,
      count: getLanguageTotalCount(language.langCode),
      name: getLanguageCodeName(language.langCode, locale),
    })
  )
  languageCountWalletsData.sort((a, b) => a.name.localeCompare(b.name))
  return languageCountWalletsData
}

function getActiveFilterKeys(filters: FilterOption[]): string[] {
  const keys: string[] = []
  filters.forEach((filter) => {
    filter.items.forEach((item) => {
      if (item.inputState === true && item.options.length === 0) {
        keys.push(item.filterKey)
      }
      if (item.options?.length > 0) {
        item.options.forEach((option) => {
          if (option.inputState === true) {
            keys.push(option.filterKey)
          }
        })
      }
    })
  })
  return keys
}

export const filterFn = (data: WalletRow[], filters: FilterOption[]) => {
  let selectedLanguage: string = ""
  let selectedLayer2: ChainName[] = []

  const activeFilterKeys = getActiveFilterKeys(filters)

  for (const filter of filters) {
    for (const item of filter.items) {
      if (item.filterKey === "languages") {
        selectedLanguage = item.inputState as string
      } else if (item.filterKey === "layer_2_support") {
        selectedLayer2 = (item.inputState as ChainName[]) || []
      }
    }
  }

  return data.filter((wallet) => {
    // Check language support
    const matchesLanguage = wallet.languages_supported.includes(
      selectedLanguage as Lang
    )

    // Check layer 2 support (empty array means no filter applied)
    const matchesLayer2 =
      selectedLayer2.length === 0 ||
      selectedLayer2.every((chain) => wallet.supported_chains.includes(chain))

    // Check active filter keys
    const matchesActiveFilters = activeFilterKeys.every((key) => wallet[key])

    return matchesLanguage && matchesLayer2 && matchesActiveFilters
  })
}
